#include <ptgui/ptgui.h>
#include <ptgui/font.h>
#include <ptgui/dc.h>

static ptgui_list_t _ptgui_font_list;
static struct ptgui_font *ptgui_default_font;

extern struct ptgui_font ptgui_font_asc16;
extern struct ptgui_font ptgui_font_arial16;
extern struct ptgui_font ptgui_font_asc12;
extern struct ptgui_font ptgui_font_arial12;
#ifdef PTGUI_USING_FONTHZ
extern struct ptgui_font ptgui_font_hz16;
extern struct ptgui_font ptgui_font_hz12;
#endif

void ptgui_font_system_init()
{
	ptgui_list_init(&(_ptgui_font_list));

	/* set default font to NULL */
	ptgui_default_font = RT_NULL;

#ifdef PTGUI_USING_FONT16
	ptgui_font_system_add_font(&ptgui_font_asc16);
#ifdef PTGUI_USING_FONTHZ
	ptgui_font_system_add_font(&ptgui_font_hz16);
#endif
#endif

#ifdef PTGUI_FEATHER_USING_FONT12
	ptgui_font_system_add_font(&ptgui_font_asc12);
#ifdef PTGUI_USING_FONTHZ
	ptgui_font_system_add_font(&ptgui_font_hz12);
#endif
#endif

#ifdef PTGUI_FEATHER_USING_FONT12
	if (ptgui_default_font == RT_NULL) {
		ptgui_font_set_defaut(&ptgui_font_asc12);
	}
#endif
#ifdef PTGUI_USING_FONT16
	if (ptgui_default_font == RT_NULL) {
		ptgui_font_set_defaut(&ptgui_font_asc16);
	}
#endif
}

void ptgui_font_system_add_font(struct ptgui_font *font)
{
	ptgui_list_init(&(font->list));
	ptgui_list_append(&_ptgui_font_list, &(font->list));

	/* init font */
	if (font->engine->font_init != RT_NULL) {
		font->engine->font_init(font);
	}

	/* first refer, load it */
	if (font->engine->font_load != RT_NULL) {
		font->engine->font_load(font);
	}
}

void ptgui_font_system_remove_font(struct ptgui_font *font)
{
	ptgui_list_remove(&_ptgui_font_list, &(font->list));
}

struct ptgui_font * ptgui_font_default()
{
	return ptgui_default_font;
}

void ptgui_font_set_defaut(struct ptgui_font *font)
{
	ptgui_default_font = font;
}

struct ptgui_font * ptgui_font_refer(const rt_uint8_t *family, rt_uint16_t height)
{
	/* search font */
	struct ptgui_list_node *node;
	struct ptgui_font *font;

	ptgui_list_foreach(node, &_ptgui_font_list)
	{
		font = ptgui_list_entry(node, struct ptgui_font, list);
		if ((rt_strncmp((const rt_int8_t *)font->family, (const rt_int8_t *)family, PTGUI_NAME_MAX) == 0) &&
			font->height == height) {
			font->refer_count ++;
			return font;
		}
	}

	return RT_NULL;
}

void ptgui_font_derefer(struct ptgui_font *font)
{
	RT_ASSERT(font != RT_NULL);

	font->refer_count --;

	/* no refer, remove font */
	if (font->refer_count == 0) {
		ptgui_font_system_remove_font(font);
	}
}

/* draw a text */
void ptgui_font_draw(struct ptgui_font *font, struct ptgui_dc *dc, const rt_int8_t *text, rt_ubase_t len,
					 struct ptgui_rect *rect)
{
	RT_ASSERT(font != RT_NULL);

	if (font->engine != RT_NULL && font->engine->font_draw_text != RT_NULL) {
		font->engine->font_draw_text(font, dc, text, len, rect);
	}
}

int ptgui_font_get_string_width(struct ptgui_font *font, const rt_int8_t *text)
{
	ptgui_rect_t rect;

	/* get metrics */
	ptgui_font_get_metrics(font, text, &rect);

	return rect.x2 - rect.x1;
}

void ptgui_font_get_metrics(struct ptgui_font *font, const rt_int8_t *text, ptgui_rect_t *rect)
{
	RT_ASSERT(font != RT_NULL);

	if (font->engine != RT_NULL && font->engine->font_get_metrics != RT_NULL) {
		font->engine->font_get_metrics(font, text, rect);
	}
	else {
		/* no font engine found, set rect to zero */
		rt_memset(rect, 0, sizeof(ptgui_rect_t));
	}
}

static void ptgui_bitmap_font_draw_text(struct ptgui_font *font, struct ptgui_dc *dc, const rt_int8_t *text,
										rt_ubase_t len, struct ptgui_rect *rect);
static void ptgui_bitmap_font_get_metrics(struct ptgui_font *font, const rt_int8_t *text, ptgui_rect_t *rect);
struct ptgui_font_engine bmp_font_engine =
{
	RT_NULL,
	RT_NULL,
	ptgui_bitmap_font_draw_text,
	ptgui_bitmap_font_get_metrics
};

void ptgui_bitmap_font_draw_char(struct ptgui_font_bitmap *font, struct ptgui_dc *dc, const rt_int8_t ch,
								 ptgui_rect_t *rect)
{
	const rt_uint8_t *font_ptr;
	rt_uint16_t x,
				y,
				w,
				h;
	register rt_base_t i,
					   j;

	x = rect->x1;
	y = rect->y1;

	/* check first and last rt_int8_t */
	if (ch < font->first_char || ch > font->last_char) {
		return;
	}
	font_ptr = font->bmp + (ch - font->first_char) * font->height;

	w = (font->width + x > rect->x2) ? rect->x2 - rect->x1 : font->width;
	h = (font->height + y > rect->y2) ? rect->y2 - rect->y1 : font->height;

	for (i = 0; i < h; i ++) {
		for (j = 0; j < w; j ++) {
			if (((font_ptr[i] >> (7 - j)) & 0x01) != 0) {
				/* draw a pixel */
				ptgui_dc_draw_point(dc, j + x, i + y);
			}
		}
	}
}

static void ptgui_bitmap_font_draw_text(struct ptgui_font *font, struct ptgui_dc *dc, const rt_int8_t *text,
										rt_ubase_t len, struct ptgui_rect *rect)
{
	struct ptgui_font_bitmap *bmp_font = (struct ptgui_font_bitmap *)(font->data);

	RT_ASSERT(bmp_font != RT_NULL);

	while (len-- && rect->x1 < rect->x2) {
		ptgui_bitmap_font_draw_char(bmp_font, dc, *text, rect);

		/* move x to next character */
		rect->x1 += bmp_font->width;
		text ++;
	}
}

static void ptgui_bitmap_font_get_metrics(struct ptgui_font *font, const rt_int8_t *text, ptgui_rect_t *rect)
{
	struct ptgui_font_bitmap *bmp_font = (struct ptgui_font_bitmap *)(font->data);

	RT_ASSERT(bmp_font != RT_NULL);

	/* set metrics rect */
	rect->x1 = rect->y1 = 0;
	rect->x2 = bmp_font->width * (rt_int16_t)rt_strlen((const rt_int8_t *)text);
	rect->y2 = bmp_font->height;
}
